#include "scheduler.h"
#include "hook.h"
#include <utility>
#include "log.h"
#include "macro.h"

namespace yuechuan {
    static yuechuan::Logger::ptr g_logger = YUECHUAN_LOG_NAME("root");
    static thread_local Scheduler *t_scheduler = nullptr;                            // 当前的调度器
    static thread_local Fiber *t_scheduler_fiber = nullptr;                            // 当前调度器的协程

    Scheduler::Scheduler(size_t threads, bool use_caller, std::string name)
            : m_name(std::move(name)) {
        YUECHUAN_ASSERT(threads > 0);
        if (use_caller) {
            Fiber::GetThis();                            // 对于没有创建协程时，调用该方法会创建一个主协程
            --threads;                                    // 主协程作为调度器中协程计算
            // 一个线程中只允许有一个调度器，当执行GetThis，返回的是当前线程的协程调度器，为NullPtr是正确的
            YUECHUAN_ASSERT(GetThis() == nullptr);
            t_scheduler = this;                                        // 将线程的调度器设置为当前对象
            // 和上面的Fiber::GetThis()区别，这个主协程是作为协程调度器中用于执行回调函数的协程
            m_rootFiber.reset(new yuechuan::Fiber(std::bind(&Scheduler::run, this), 0,true));
            yuechuan::Thread::setName(m_name);
            t_scheduler_fiber = m_rootFiber.get();    // 需要将当前线程中的主协程设置为调度器中的主协程，rootFiber是创建调度器的线程的执行run方法的主协程
            m_rootThread = yuechuan::GetThreadId();    // 创建调度器的线程就作为调度器的主线程，将其id设置为主线程id即可
            m_threadIds.push_back(m_rootThread);
        } else {
            m_rootThread = -1;                                        /// 不使用useCall参数，说明不是需要将当前线程配置到调度器中的线程池中，将主线程id置为-1
        }
        m_threadCount = threads;
    }

    Scheduler::~Scheduler() {
        YUECHUAN_ASSERT(m_stopping);
        if (GetThis() == this) {
            t_scheduler = nullptr;
        }
    }

    const std::string Scheduler::getName() const { return m_name; }

    Scheduler *Scheduler::GetThis() {
        return t_scheduler;
    }

    Fiber *Scheduler::GetMainFiber() {
        return t_scheduler_fiber;
    }

    void Scheduler::run() {
        YUECHUAN_LOG_INFO(g_logger) << " run";
        set_hook_enable(true);
        setThis();
        if (yuechuan::GetThreadId() != m_rootThread) {
            t_scheduler_fiber = Fiber::GetThis().get();        /// 调用协程调度器的线程不为协程调度器的主线程时，
            /// 将协程调度器的主协程设置为当前线程的主协程
        }
        /// 对于调度器不将其线程终止，而创建的轮空线程，其是虚函数，实现方法针对不同子类来限定
        Fiber::ptr idle_fiber(new Fiber(std::bind(&Scheduler::idle, this)));
        Fiber::ptr cb_fiber;

        YUECHUAN_LOG_INFO(g_logger) << "Idle_fiber Create id [ " << idle_fiber->getId() << " ]";
        /// 可执行的任务，下面就是从任务队列中找到一个可执行的将其赋值，这里的任务指的是线程下的协程
        Task fc;
        while (true) {
            fc.reset();
            bool tickle_me = false;                                                // 是否唤醒任务
            bool is_active = false;
            /// 这个加锁的区间是用于取出一个待执行的任务，将其存放到fc中
            {
//                MutexType::Lock lock(m_mutex);
                auto it = m_fibers.begin();
                while (it != m_fibers.end()) {        /// 可以执行run方法的任务的前提是：线程已经创建，且不是创建调度器的线程

                    /// 任务已经指定了线程，且不等于当前执行run的线程，那么就不会在当前线程去执行该任务
                    if (it->thread != -1 && it->thread != yuechuan::GetThreadId()) {
                        ++it;
                        tickle_me = true;            /// 通知其他线程，唤醒其他线程，让其执行
                        continue;
                    }
                    YUECHUAN_ASSERT(it->fiber || it->cb);
                    if (it->fiber && it->fiber->getState() == Fiber::EXEC) {            // 还要确定其协程存在，且状态不能是已经运行的状态
                        ++it;
                        continue;
                    }
                    fc = *it;
                    m_fibers.erase(it++);   /// 已经从m_fibers中将任务取出放到fc中了，下面就要对其进行调度，则任务队列中就要将其删除了
                    ++m_activateThreadCount;
                    is_active = true;
                    break;
                }
                tickle_me |= it != m_fibers.end();
            }
            if (tickle_me) {
                tickle();                    // 唤醒其他线程，这个变量只会在任务指定了线程且不为当前线程时，或者没有指定线程时赋值为true
            }
            /// 取得的任务是协程，且状态不为结束或者异常，需要将其swapIn()进行执行
            if (fc.fiber && (fc.fiber->getState() != Fiber::TERM && fc.fiber->getState() != Fiber::EXCE)) {
                fc.fiber->swapIn();
                --m_activateThreadCount;

                /// 协程调度返回，当状态为READY时，需要将其再次加入到等待队列中，等待下次调度
                if (fc.fiber->getState() == Fiber::READY) {
                    schedule(fc.fiber);
                }
                    /// 协程调度返回，但状态不为READY，且不为结束，和异常，将其状态设置为HOLD，等待下次swapIn()
                else if (fc.fiber->getState() != Fiber::TERM && fc.fiber->getState() != Fiber::EXCE) {
                    fc.fiber->m_state = Fiber::HOLD;
                }
                /// 一个任务调度完成之后需要将fc进行重置，方便下次循环中取得下一个任务进行调度
                fc.reset();

            }
                /// 任务队列中不是协程而是一个函数时，利用其函数创建一个协程，然后swapIn()进行调度
            else if (fc.cb) {
                if (cb_fiber) {
                    cb_fiber->reset(fc.cb);                        // 对象的reset成员函数
                } else {
                    cb_fiber.reset(new Fiber(fc.cb));            // 智能指针的reset函数
                }
                fc.reset();
                cb_fiber->swapIn();
                --m_activateThreadCount;

                /// 协程调度返回，当状态为READY时，需要将其加入到等待队列中，等待下次调度
                if (cb_fiber->getState() == Fiber::READY) {
                    schedule(cb_fiber);
                    cb_fiber.reset();
                }
                    /// 协程调度返回，当状态为结束或者异常时说明该次调度完成或者失败，直接将该协程进行重置即可
                else if (cb_fiber->getState() == Fiber::EXCE ||
                         cb_fiber->getState() == Fiber::TERM) {
                    cb_fiber->reset(nullptr);
                }
                    /// 协程调度返回，当状态不为READY，结束，异常时，协程调度返回，将其设置为HOLD，等待下次swapIn(),然后再将该协程对象进行重置
                else {
                    cb_fiber->m_state = Fiber::HOLD;
                    cb_fiber.reset();
                }
            }
                /// 取得的任务既不是协程，也不是函数，说明没有取到，那么协程调度器进入idle轮询
            else {
                /// 这个is_active 在任何一次取得任务时都会将其设置为true，然后协程调度器就会陷入轮询协程中
                if (is_active) {
                    --m_activateThreadCount;
                    continue;
                }
                /// 轮询协程状态为结束，那么协程调度器执行run方法就已经结束了，直接break跳出循环
                if (idle_fiber->getState() == Fiber::TERM) {
                    YUECHUAN_LOG_INFO(g_logger) << " idle fiber TERM";
                    break;
                }
                ++m_idleThreadCount;
                idle_fiber->swapIn();                            // 轮转协程的调用
                --m_idleThreadCount;
                if (idle_fiber->getState() != Fiber::TERM && idle_fiber->getState() != Fiber::EXCE) {
                    idle_fiber->m_state = Fiber::HOLD;
                }
            }
        }
    }

    void Scheduler::start() {
        MutexType::Lock lock(m_mutex);
        if (!m_stopping) {                                            // 当stopping为False时，其已经是非停止状态，已经在运行了，就不需要执行start方法

            return;
        }
        m_stopping = false;
        YUECHUAN_ASSERT(m_threads.empty());

        m_threads.resize(m_threadCount);                            // 根据当前线程的数量来创建线程池
        for (size_t i = 0; i < m_threadCount; ++i) {
            m_threads[i].reset(new Thread(std::bind(&Scheduler::run, this),
                                          m_name + "_" + std::to_string(i)));    // 每个线程都绑定run回调函数
            m_threadIds.push_back(m_threads[i]->getId());
            // YUECHUAN_LOG_INFO(g_logger) << "Create a new Thread "<< i;
        }
        lock.unlock();                                                // 上面加的锁，在对vector操作完成后需要将其打开
    }

    void Scheduler::stop() {
        /*
            对于调度器内部的主协程要在其线程内部执行stop
            对于其他协程可以在任意位置执行stop函数来进行释放

        */
        m_autoStop = true;
        if (m_rootFiber && m_threadCount == 0 &&
            (m_rootFiber->getState() == Fiber::TERM || m_rootFiber->getState() == Fiber::INIT)) {

            YUECHUAN_LOG_INFO(g_logger) << "scheduler->mainFiber [ " << this << " ] stopped";
            m_stopping = true;        // 当m_threadCount为0时，表示该线程池中没有线程了，当执行停止时，将stopping设置为true即可
            if (stopping()) {
                return;
            }
        }
        if (m_rootThread != -1) {         // 主线程号不是-1说明是别的线程调用了stop方法，要进行判断，主线程是否等于当前对象
            YUECHUAN_ASSERT(GetThis() == this);
        } else {
            YUECHUAN_ASSERT(GetThis() != this);
        }
        m_stopping = true;
        for (size_t i = 0; i < m_threadCount; ++i) {
            tickle();                                         // 唤醒线程的功能,当线程执行完毕回调函数后就会自动释放
        }
        if (m_rootFiber) {
            tickle();
        }

        if (m_rootFiber) {
            if (!stopping()) {
                m_rootFiber->call();
            }
        }
        std::vector<Thread::ptr> thrs;
        {
            MutexType::Lock locale_t(m_mutex);
            thrs.swap(m_threads);
        }

        for (auto &i : thrs) {
            i->join();
        }

    }

    void Scheduler::setThis() {
        t_scheduler = this;
    }

    void Scheduler::tickle() {
        YUECHUAN_LOG_INFO(g_logger) << "tickle";

    }

    bool Scheduler::stopping() {
        MutexType::Lock lock(m_mutex);
        return m_autoStop && m_stopping && m_fibers.empty() && m_activateThreadCount == 0;

    }

    void Scheduler::idle() {
        YUECHUAN_LOG_INFO(g_logger) << "idling~ ";
        while (!stopping()) {

            yuechuan::Fiber::YieldToHold();
        }

    }
} // namespace yuechuan
